package com.bigknow.appstore.web.framework.controller;

import com.bigknow.appstore.common.AppStoreRuntimeException;
import com.bigknow.appstore.proxy.service.ProxyManager;
import com.bigknow.appstore.web.base.service.IAuditLogService;
import com.bigknow.appstore.web.base.service.IPermissionService;
import com.bigknow.appstore.web.framework.util.GlobalConstant;
import com.bigknow.appstore.web.framework.vo.ResultInfo;
import com.bigknow.appstore.common.util.StringUtils;
import com.bigknow.appstore.web.base.entity.AuditLog;
import com.bigknow.appstore.web.framework.entity.IEntity;
import com.bigknow.appstore.web.framework.service.ICRUDService;
import com.bigknow.appstore.web.framework.vo.VoFactory;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.vo.NormalExcelConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Rest json必须要有返回值
 * 路径前带rest的路径，作为请求数据接口，返回对象。
 * 普通路径，作为页面跳转或者执行操作，返回ModelAndView
 * Created by TwelveFoldEr on 2016/3/8.
 */
public abstract class AbstractRestController<T extends IEntity, E extends ICRUDService<T>> extends BaseController {

    @Autowired
    protected E service;
    @Autowired
    protected IAuditLogService auditLogService;

    @Autowired
    protected IPermissionService permissionService;

    protected abstract String getBasePath();

    protected abstract String getBaseName();

    protected String getUrl(String name) {
        return getBasePath() + "/" + getBaseName() + "/" + getBaseName() + name;
    }

    public enum SearchPattern {
        EQUAL, FUZZY
    }

    protected SearchPattern getSearchPattern() {
        return SearchPattern.FUZZY;
    }

    private String getListUrl() {
        return getUrl("List");
    }

    private String getFormUrl() {
        return getUrl("Form");
    }

    protected String getFromUrlWithAttr(String attr, Map<String, Object> map) {
        if (!org.apache.commons.lang3.StringUtils.isEmpty(attr)) {
            return getUrl(attr.toUpperCase().charAt(0) + attr.substring(1, attr.length()).toLowerCase() + "Form");
        }
        return "";
    }

    @RequestMapping(value = "/rest/findAll", method = RequestMethod.GET)
    public List<T> findAll() {
        return service.findAll();
    }

    @RequestMapping(value = "/rest/get/{id}", method = RequestMethod.GET)
    public IEntity get(@PathVariable String id) {
        return service.get(id);
    }

    @RequestMapping(value = "/rest/findFirst", method = RequestMethod.GET)
    public IEntity findFirst(T t) {
        return service.findFirst(t);
    }

    @RequestMapping(value = "/rest/save", method = RequestMethod.POST)
    public ResultInfo save(T t) {
        //当id不为空，并且id存在的时候再做update，否则执行insert
        //先执行update，如果没有update任何数据再做insert，就不执行return
        boolean hasId = StringUtils.isNoneBlank(t.getId());
        try {
            if (service.save(t) == 0) {
                return VoFactory.newResultInfo(ResultInfo.ERROR, springUtils.getMessage(GlobalConstant.UPDATE_NONE));
            } else {
                if (hasId) {
                    addAuditLog(t, AuditLog.OperationType.UPDATE);
                    return VoFactory.newResultInfo(ResultInfo.SUCCESS, springUtils.getMessage(GlobalConstant.UPDATE_SUCCESS));
                } else {
                    addAuditLog(t, AuditLog.OperationType.INSERT);
                    return VoFactory.newResultInfo(ResultInfo.SUCCESS, springUtils.getMessage(GlobalConstant.INSERT_SUCCESS));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return VoFactory.newResultInfo(ResultInfo.ERROR, e.getMessage());
        }
    }

    @RequestMapping(value = "/rest/insert", method = RequestMethod.POST)
    public ResultInfo insert(T t) {
        try {
            if (service.insert(t) == 0) {
                return VoFactory.newResultInfo(ResultInfo.ERROR, springUtils.getMessage(GlobalConstant.INSERT_NONE));
            }
        } catch (Exception e) {
//            throw new AppStoreRuntimeException(e);
            e.printStackTrace();
            return VoFactory.newResultInfo(ResultInfo.ERROR, e.getMessage());
        }

        addAuditLog(t, AuditLog.OperationType.INSERT);
        return VoFactory.newResultInfo(ResultInfo.SUCCESS, springUtils.getMessage(GlobalConstant.INSERT_SUCCESS));
    }

    @RequestMapping(value = "/rest/update", method = RequestMethod.POST)
    public ResultInfo update(T t) {
        try {
            if (service.update(t) == 0) {
                return VoFactory.newResultInfo(ResultInfo.ERROR, springUtils.getMessage(GlobalConstant.UPDATE_NONE));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return VoFactory.newResultInfo(ResultInfo.ERROR, e.getMessage());
        }

        addAuditLog(t, AuditLog.OperationType.UPDATE);
        return VoFactory.newResultInfo(ResultInfo.SUCCESS, springUtils.getMessage(GlobalConstant.UPDATE_SUCCESS));
    }

    @RequestMapping(value = "/rest/delete/{id}", method = RequestMethod.DELETE)
    public ResultInfo delete(@PathVariable String id) {
        T t = service.get(id);
        try {
            if (service.delete(id) == 0) {
                return VoFactory.newResultInfo(ResultInfo.ERROR, springUtils.getMessage(GlobalConstant.DELETE_NONE));
            }
        } catch (Exception e) {
            return VoFactory.newResultInfo(ResultInfo.ERROR, e.getMessage());
        }
        addAuditLog(t, AuditLog.OperationType.DELETE);

        return VoFactory.newResultInfo(ResultInfo.SUCCESS, springUtils.getMessage(GlobalConstant.DELETE_SUCCESS));
    }

    /**
     * 非rest方法,用于返回list页面,若果采用json方式
     *
     * @param t
     * @param pageIndex
     * @param pageSize
     * @return
     */
    @RequestMapping(value = "/list")
    public ModelAndView list(T t, Integer pageIndex, Integer pageSize) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            pageIndex = pageIndex == null ? GlobalConstant.DEFAULT_PAGE_INDEX : pageIndex;
            pageSize = pageSize == null ? GlobalConstant.DEFAULT_PAGE_SIZE : pageSize;
            switch (getSearchPattern()) {
                case EQUAL:
                    map.put("page", this.service.find(t, pageIndex, pageSize));
                    map.put("fuzzyField", "");
                    map.put("record", t);
                    break;
                case FUZZY:
                default:
                    map.put("page", this.service.findWithFuzzy(t, pageIndex, pageSize));
                    map.put("fuzzyField", t.getFuzzyField());
                    map.put("record", t);
                    break;
            }
        } catch (Exception e) {
            this.logger.warn("List page error.", e);
            e.printStackTrace();
        }

        return new ModelAndView(getListUrl(), map);
    }

    @RequestMapping(value = "/innerList")
    public ModelAndView monitorStateList(T t, Integer pageIndex, Integer pageSize) {
        ModelAndView old = this.list(t, pageIndex, pageSize);
        ModelAndView result = new ModelAndView(getUrl("InnerList"), old.getModel());
        return result;
    }

    /**
     * @return
     */
    @RequestMapping(value = "/form")
    public ModelAndView newForm() {
        Map<String, Object> map = new HashMap<String, Object>();
//        map.put("record", null);
        map.put("actionType", "new");
        return new ModelAndView(getFormUrl(), map);
    }

    /**
     * @param actionType default view.Other edit
     * @param id
     * @return
     */
    @RequestMapping(value = "/form/{actionType}/{id}")
    public ModelAndView form(@PathVariable String actionType, @PathVariable String id) {
        Map<String, Object> map = new HashMap<String, Object>();
        if (id != null && !id.trim().equalsIgnoreCase(""))
            map.put("record", service.get(id));
        if (actionType == null || actionType.trim().equalsIgnoreCase("")) actionType = "view";
        map.put("actionType", actionType);
        return new ModelAndView(getFormUrl(), map);
    }

    /**
     * 根据记录的不同属性跳转不同的编辑页面
     *
     * @param actionType default view.Other edit
     * @param id
     * @param attr
     * @return
     */
    @RequestMapping(value = "/form/{actionType}/{id}/{attr}")
    public ModelAndView form(@PathVariable String actionType, @PathVariable String id, @PathVariable String attr) {
        Map<String, Object> map = new HashMap<String, Object>();
        if (id != null && !id.trim().equalsIgnoreCase(""))
            map.put("record", service.get(id));
        if (actionType == null || actionType.trim().equalsIgnoreCase("")) actionType = "view";
        map.put("actionType", actionType);
        injectModelToMap(map);
        return new ModelAndView(getFromUrlWithAttr(attr, map), map);
    }

    @RequestMapping(value = "/exportAll/{type}")
    public ModelAndView exportXls(T t, @PathVariable String type, ModelMap map) {
        List<T> entities = this.service.findAll();

        map.put(NormalExcelConstants.FILE_NAME, getExportFileName());
        map.put(NormalExcelConstants.CLASS, t.getClass());
        map.put(NormalExcelConstants.PARAMS, new ExportParams("应用商店", "导出信息"));
        map.put(NormalExcelConstants.DATA_LIST, entities);
        return new ModelAndView(NormalExcelConstants.JEECG_EXCEL_VIEW);
    }

    protected void addAuditLog(T entity, AuditLog.OperationType operationType) {
        auditLogService.addByEntity(entity, this.getClass(), operationType);
    }

    protected String getExportFileName() {
        return UUID.randomUUID().toString();
    }

    protected void injectModelToMap(Map<String, Object> map) {

    }

    protected ResultInfo doUpdate(T t) {
        try {
            if (service.save(t) == 0)
                return VoFactory.newResultInfo(ResultInfo.ERROR, springUtils.getMessage(GlobalConstant.UPDATE_NONE));
        } catch (Exception e) {
            e.printStackTrace();
            return VoFactory.newResultInfo(ResultInfo.ERROR, e.getMessage());
        }
        return VoFactory.newResultInfo(ResultInfo.SUCCESS, springUtils.getMessage(GlobalConstant.UPDATE_SUCCESS));
    }

}
